package com.sfzd5.exam.helplibrary.exam;

import com.google.gson.JsonObject;
import com.itextpdf.awt.AsianFontMapper;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.BaseFont;
import com.sfzd5.exam.helplibrary.exam.answersheet.*;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 输出内容区域 15 20 180 257
 */
public class MakePage {
    private BufferedImage bi;
    Graphics2DMM g;

    public float curTopm = 0f;

    public void close(){
        g.dispose();
        bi = null;
    }
    /**
     * 打印到图片
     */
    public MakePage(){
        PageA4.dpi = 72;
        int pageWidth = PageA4.getPageWidth();
        int pageHeight = PageA4.getPageHeight();

        bi = new BufferedImage(pageWidth, pageHeight, BufferedImage.TYPE_INT_RGB);

        // createGraphics	// 创建图层
        Graphics2D g2d = bi.createGraphics();
        try {
            AsianFontMapper mapper = new AsianFontMapper("STSong-Light", "UniGB-UCS2-H");
            BaseFont baseFontChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
            Font fontChinese = mapper.pdfToAwt(baseFontChinese, 12);
            g2d.setFont(fontChinese);
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        g2d.setBackground(Color.WHITE);    // 设置背景颜色（白色）
        g2d.clearRect(0, 0, pageWidth, pageHeight);    // 矩形 X、Y、width、height
        g2d.setColor(Color.BLACK);    // 设置图像颜色（黑色）

        g = new Graphics2DMM(g2d);

        //写定位点
        drawLocatePoints();
        curTopm += PageA4.marginTopBottom;
    }

    /**
     * 打印到指写设备，一般是pdf
     * @param g
     */
    public MakePage(Graphics2DMM g){
        PageA4.dpi = 72;
        this.g =g;
        //写定位点
        drawLocatePoints();
        curTopm += PageA4.marginTopBottom;
    }

    /**
     * 画页面
     * @param page
     */
    public void drawPage(ExamInof examInof, Page page, long taskId, int stuId){
        if(page.hasExamInfo){
            drawInfo(examInof.examName, taskId, stuId);
        }

        for(AreaDrawInfo drawInfo : page.areaDrawInfoList){
            Area area = examInof.areaList.get(drawInfo.areaIdx);
            if (area.areaType == Area.AreaType.Choice) {
                drawOptionsArea(examInof, drawInfo, 1);
            } else if (area.areaType == Area.AreaType.Blank) {
                drawBlanksArea(examInof, drawInfo, 1);
            } else if (area.areaType == Area.AreaType.Expound) {
                drawExpoundArea(examInof, drawInfo, 1);
            }
        }
    }

    public int drawOptionsArea(ExamInof examInof, AreaDrawInfo drawInfo, int qid) {
        ArrayList<ChoiceAnswerSheetQuestion> choiceQuestionList = new ArrayList<>();
        Area area = examInof.areaList.get(drawInfo.areaIdx);
        for (AnswerSheetQuestion answerSheetQuestion : area.answerSheetQuestionList) {
            choiceQuestionList.add(new ChoiceAnswerSheetQuestion(answerSheetQuestion));
        }

        // 是首行，则插入标题
        if(drawInfo.startLine==0){
            g.drawString(area.title, PageA4.marginLeftRightm, curTopm + 2, PageA4.lineHightm - 3);
            curTopm += PageA4.lineHightm;
        }

        int lineCount = drawInfo.endLine-drawInfo.startLine;
        //画框
        g.drawRect(PageA4.marginLeftRightm, curTopm, PageA4.contextWidthm(), PageA4.optionLineHeightm * lineCount + PageA4.areaMarginm * 2, PageA4.boardWidthm);

        // 要插入的题目数
        int qc = area.answerSheetQuestionList.size() - drawInfo.startLine * 4;
        int qc2 = (drawInfo.endLine - drawInfo.startLine) * 4;
        qc = qc<qc2 ? qc : qc2;

        float y = curTopm + PageA4.areaMarginm * 2;
        float x = PageA4.marginLeftRightm + PageA4.areaMarginm;
        int h = 0;
        int q = 0;
        for(int startLine = drawInfo.startLine; startLine < drawInfo.endLine; startLine++){
            // 每行四个题
            for(int i = 0; i<4; i++){
                drawOption(x + 44 * i, y + PageA4.optionLineHeightm * h - 1, qid);
                q++;
                qid ++;
                if(q>=qc) break;
            }
            h++;
            if(q>=qc) break;
        }

        // 提升当前位置，内容高度+域名上下距离
        curTopm += PageA4.optionLineHeightm * lineCount + PageA4.areaMarginm * 2;
        return qid;
    }

    public void drawOption(float xm, float ym, int index){
        //需要计算出字体大小 3mm
        //[]和空格宽度相同
        //0123456789宽度相同
        //ABCD宽度不同，可以输出文字，让宽度相同
        //字号3mm，选项宽7mm居中，前边数字宽7mm左对齐

        g.drawString(String.valueOf(index), xm, ym,3);
        //输入位置，横坐标
        float p = xm + 7; //加上题号7mm
        char[] cs = {'A','B','C','D'};
        for(char c : cs){
            g.drawTtitleString("[", p, ym, 1, 3);
            p+=1;
            //g.drawTtitleString(String.valueOf(c), p, ym, 3, 3);
            p+=3;
            g.drawTtitleString("]", p, ym, 1, 3);
            p+=1;
            //2mm选项间距
            p+=2;
        }
    }

    public List<ExpoundLine> getExpoundLines(int qid, List<ExpoundAnswerSheetQuestion> expoundQuestionList){
        List<ExpoundLine> lines = new ArrayList<>();
        //大题号与小题号
        for(ExpoundAnswerSheetQuestion eq: expoundQuestionList){
            if(eq.getChildQuestionList().size()==1){
                ExpoundChildQuestion ecq = eq.getChildQuestionList().get(0);
                for(int i = 0; i< ecq.lineNum; i++){
                    ExpoundLine line = new ExpoundLine();
                    line.hasQid = i==0;
                    line.withLine = ecq.withLine;
                    line.chileQid = 0;
                    lines.add(line);
                }
            } else {
                int ci = 0;
                for (int i = 0; i< eq.getChildQuestionList().size(); i++) {
                    ci++;
                    ExpoundChildQuestion ecq = eq.getChildQuestionList().get(i);
                    for(int j = 0; j< ecq.lineNum; j++){
                        ExpoundLine line = new ExpoundLine();
                        line.hasQid = ((i==0) && (j==0));
                        line.withLine = ecq.withLine;
                        line.chileQid = j==0 ? ci : 0;
                        lines.add(line);
                    }
                }
            }
            qid++;
        }
        return lines;
    }

    public List<BlankLine> getBlankLines(int qid, List<BlankAnswerSheetQuestion> blankList) {
        // 行宽
        //float pw = PageA4.contextWidthm() - 2 * PageA4.areaMarginm;
        //FontMetricsMM fm = g.getFontMetricsMM(3);
        //每 _ 宽度
        //float _w = fm.getWidth("______________________________") / 30;

        List<BlankLine> lines = new ArrayList<>();
        //int lineWidth = (int) Math.floor(pw / _w);
        int lineWidth = 99;//每行有99个_ ，每空
        StringBuilder sb = new StringBuilder();

        //逐题逐空逐标题判断是否超行，题号与首空为一整体
        for (BlankAnswerSheetQuestion blankQuestion : blankList) {
            //每题都要新开一行，所以清空
            sb.delete(0, sb.length());//清空

            //新小题的第一行，每个题的答题开始都是新行，每个空前加三个字符 [ ]，让学生在这里打对号
            BlankLine blankLine = new BlankLine();
            if (qid < 10) sb.append(" ");
            sb.append(qid).append(".");
            blankLine.hasQid = true;

            for (BlankInfo blankInfo : blankQuestion.getBlankInfoList()) {
                //计算还可以添加多少个空格
                int canAdd_Count = lineWidth - sb.length();

                //本行能放得下，就放在本行
                if(canAdd_Count>=blankInfo.width + 3) {
                    sb.append("[ ]");
                    for (int bi = 0; bi < blankInfo.width; bi++)
                        sb.append("_");

                } else {
                    //本行放不下，不可以将本空全部添加到本行，计算剩余的_字符数
                    int sykg = blankInfo.width;

                    // 如果本行最少有最少有15个位置，并且转到下行的字数超过7个本空格字节数大于15，
                    if(canAdd_Count>15){
                        sb.append("[ ]");
                        canAdd_Count -= 3;
                        for (int bi = 0; bi < canAdd_Count; bi++)
                            sb.append("_");
                        //本空剩余的部分
                        sykg -= canAdd_Count;
                    }

                    //如果剩余_个数不为0则一直添加到完，支持超长空格
                    while (sykg>0) {
                        //将前面添加的内容添加到列表里
                        blankLine.str = sb.toString();
                        lines.add(blankLine);

                        //创建一个新行
                        blankLine = new BlankLine();
                        sb.delete(0, sb.length());//清空

                        //取小值，也就是最多可以添加的字符数，并添加
                        int ccc = Math.min(lineWidth, sykg); // ? sykg : canAdd_Count;
                        for (int bi = 0; bi < ccc; bi++)
                            sb.append("_");
                        sykg -= ccc;
                    }
                }

                //添加一个空格隔开空
                sb.append(" ");
            }
            blankLine.str = sb.toString();
            lines.add(blankLine);
            qid++;
        }
        return lines;
    }

    public int drawBlanksArea(ExamInof examInof, AreaDrawInfo drawInfo, int qid){

        ArrayList<BlankAnswerSheetQuestion> blankList = new ArrayList<>();
        Area area = examInof.areaList.get(drawInfo.areaIdx);
        for (AnswerSheetQuestion answerSheetQuestion : area.answerSheetQuestionList) {
            blankList.add((BlankAnswerSheetQuestion)(answerSheetQuestion));
        }

        // 是首行，则插入标题
        if(drawInfo.startLine==0){
            g.drawString(area.title, PageA4.marginLeftRightm, curTopm + 2, PageA4.lineHightm - 3);
            curTopm += PageA4.lineHightm;
        }

        int lineCount = drawInfo.endLine - drawInfo.startLine;
        g.drawRect(PageA4.marginLeftRightm, curTopm, PageA4.contextWidthm(), PageA4.lineHightm * lineCount + PageA4.areaMarginm * 2, PageA4.boardWidthm);

        //分行打印
        float py = curTopm + PageA4.areaMarginm * 2; //加上题号7mm
        float px = PageA4.marginLeftRightm + PageA4.areaMarginm;

        List<BlankLine> lines = getBlankLines(qid, blankList);

        for(int i = drawInfo.startLine; i < drawInfo.endLine; i++){
            BlankLine blankLine = lines.get(i);
            if(blankLine.hasQid)
                qid++;
            g.drawString(blankLine.str, px, py + 2 + i * PageA4.lineHightm, 3);
        }

        curTopm += PageA4.lineHightm * lineCount + PageA4.areaMarginm * 2;
        return  qid;
    }

    //行高7mm,共36行
    public int drawExpoundArea(ExamInof examInof, AreaDrawInfo drawInfo, int qid){
        ArrayList<ExpoundAnswerSheetQuestion> expoundQuestionList = new ArrayList<>();
        Area area = examInof.areaList.get(drawInfo.areaIdx);
        for (AnswerSheetQuestion answerSheetQuestion : area.answerSheetQuestionList) {
            expoundQuestionList.add((ExpoundAnswerSheetQuestion)answerSheetQuestion);
        }

        // 是首行，则插入标题
        if(drawInfo.startLine==0){
            g.drawString(area.title, PageA4.marginLeftRightm, curTopm + 2, PageA4.lineHightm - 3);
            curTopm += PageA4.lineHightm;
        }

        int lineCount = drawInfo.endLine - drawInfo.startLine;
        g.drawRect(PageA4.marginLeftRightm, curTopm, PageA4.contextWidthm(), PageA4.lineHightm * lineCount + PageA4.areaMarginm * 2, PageA4.boardWidthm);

        float py = curTopm + PageA4.areaMarginm; //加上题号7mm
        float px = PageA4.marginLeftRightm + PageA4.areaMarginm;
        List<ExpoundLine> expoundLines = getExpoundLines(qid, expoundQuestionList);
        for(int i = drawInfo.startLine; i < drawInfo.endLine; i++){
            ExpoundLine expoundLine = expoundLines.get(i);

            if(expoundLine.hasQid || expoundLine.chileQid>0) {
                String th = expoundLine.hasQid ? String.valueOf(qid) + "." : "";
                th += expoundLine.chileQid>0 ? "(" + String.valueOf(expoundLine.chileQid) + ")" : "";
                float w = g.drawString(th, px, py + 3, 3);
                py += PageA4.lineHightm;
                if(expoundLine.withLine){
                    g.drawLine(px + w + 1, py, PageA4.marginLeftRightm + PageA4.contextWidthm() - PageA4.areaMarginm, py, PageA4.lineWidthm);
                }
            } else {
                py += PageA4.lineHightm;
                if(expoundLine.withLine){
                    g.drawLine(px, py, PageA4.marginLeftRightm + PageA4.contextWidthm() - PageA4.areaMarginm, py, PageA4.lineWidthm);
                }
            }

            if(expoundLine.hasQid)
                qid++;
        }
        curTopm += PageA4.lineHightm * lineCount + PageA4.areaMarginm * 2;
        return qid;
    }

    public String qrCodeStr(long taskId, long stuId){
        JsonObject json = new JsonObject();
        json.addProperty("taskId", taskId);
        json.addProperty("stuId", stuId);
        String j = json.toString();
        return j;
    }

    public void drawInfo(String examName, long taskId, int stuId){
        //标题预计20mm，总宽180，正标题10 字高8 宽140，学生信息10字高5 宽140，二维码20 左右各10空白
        g.drawTtitleString(examName, PageA4.marginLeftRightm, curTopm, 140, 8);
        String stuInfo = String.format("学生编号："+ String.format("%02d", stuId));
        g.drawTtitleString(stuInfo, PageA4.marginLeftRightm, curTopm + 12, 140, 5);
        g.drawCodeEncode(qrCodeStr(taskId, stuId), PageA4.marginLeftRightm + 140 + 10, curTopm, 25);

        curTopm += PageA4.pageInfoHightm;
    }

    private void drawLocatePoints(){
        g.fillRect(6, 11, 5, 5);
        g.fillRect(199, 11, 5, 5);
        g.fillRect(6, 281, 5, 5);
        g.fillRect(199, 281, 5, 5);

        for(int i=0; i<5; i++){
            g.fillRect(6, 49.5f*i+49.5f, 2, 5);
        }
    }

    public void saveJpeg(File file) throws IOException {
        ImageIO.write(bi, "jpg", file);
    }


    public BufferedImage getImage(){
        bi.flush();
        return bi;
    }

    public int getChoiceLineCount(Area choiceArea){
        if(choiceArea.areaType != Area.AreaType.Choice)
            return -1;
        int lineCount = (int)Math.ceil(choiceArea.answerSheetQuestionList.size()/4d);
        choiceArea.lineCount = lineCount;
        return lineCount;
    }

    public int getExpoundLineCount(Area expoundArea){
        if(expoundArea.areaType != Area.AreaType.Expound)
            return -1;
        ArrayList<ExpoundAnswerSheetQuestion> expoundList = new ArrayList<>();
        for (AnswerSheetQuestion answerSheetQuestion : expoundArea.answerSheetQuestionList) {
            expoundList.add((ExpoundAnswerSheetQuestion)answerSheetQuestion);
        }
        int lineCount = 0;
        for(ExpoundAnswerSheetQuestion eq: expoundList){
            for(ExpoundChildQuestion ecq : eq.getChildQuestionList()){
                lineCount += ecq.lineNum;
            }
        }
        expoundArea.lineCount = lineCount;
        return lineCount;
    }

    public int getBlankLineCount(Area blankArea){
        //检测类型
        if(blankArea.areaType != Area.AreaType.Blank)
            return -1;

        ArrayList<BlankAnswerSheetQuestion> blankList = new ArrayList<>();
        for (AnswerSheetQuestion answerSheetQuestion : blankArea.answerSheetQuestionList) {
            blankList.add((BlankAnswerSheetQuestion)(answerSheetQuestion));
        }
        List<BlankLine> blankLines = getBlankLines(10, blankList);
        blankArea.lineCount = blankLines.size();
        return blankArea.lineCount;
    }

    public float getAreaHeight(Area area, int line){
        int lineCount = 0;
        //区域高度计算公式 = 行高 * （行数+标题一行） + 边距*2
        if (area.areaType == Area.AreaType.Choice) {
            lineCount = getChoiceLineCount(area);
        } else if (area.areaType == Area.AreaType.Blank) {
            lineCount = getBlankLineCount(area);
        } else if (area.areaType == Area.AreaType.Expound) {
            lineCount = getExpoundLineCount(area);
        }
        if(line == 0) {
            return PageA4.lineHightm + PageA4.lineHightm * lineCount + PageA4.areaMarginm * 2;
        } else {
            return PageA4.lineHightm * (lineCount - line) + PageA4.areaMarginm * 2;
        }
    }

    /**
     * 返回0添加完毕，返回-1添加不了，返回>0行数
     * @param area
     * @param curPage
     * @param line
     * @return
     */
    private int addAreaToPage(Area area, Page curPage, int line){
        float pageFreeHeight = curPage.getFreeHeight();
        float areaHeight = getAreaHeight(area, line);

        if(pageFreeHeight>areaHeight){
            AreaDrawInfo areaDrawInfo = new AreaDrawInfo();
            areaDrawInfo.isFullArea = line==0;
            areaDrawInfo.areaIdx = area.idx;
            areaDrawInfo.startLine = line;
            areaDrawInfo.endLine = area.lineCount;
            curPage.areaDrawInfoList.add(areaDrawInfo);
            return 0;
        } else {
            if(pageFreeHeight < PageA4.lineHightm * 2 - PageA4.areaMarginm * 2 ){
                return -1;
            } else {
                int lines = area.lineCount - (int)Math.ceil((areaHeight - pageFreeHeight) / PageA4.lineHightm);
                AreaDrawInfo areaDrawInfo = new AreaDrawInfo();
                areaDrawInfo.isFullArea = false;
                areaDrawInfo.areaIdx = area.idx;
                areaDrawInfo.startLine = line;
                areaDrawInfo.endLine = line + lines;
                curPage.areaDrawInfoList.add(areaDrawInfo);
                if(area.lineCount == areaDrawInfo.endLine)
                    return 0;
                else
                    return areaDrawInfo.endLine;
            }
        }
    }

    public List<Page> makePages(ExamInof examInof){
        List<Page> pageList = new ArrayList<>();

        //创建第一个页面
        Page curPage = new Page(pageList.size(), true);
        pageList.add(curPage);

        int areaIdx = 0;
        for (Area area : examInof.areaList) {
            area.idx = areaIdx;
            areaIdx++;

            int line = 0;
            int v = addAreaToPage(area, curPage, line);

            //line==0时表示插入完了
            if(v==0)
                continue;
            else if(v<0){
                curPage = new Page(pageList.size(), false);
                pageList.add(curPage);
                line = addAreaToPage(area, curPage, line);
            } else {
                line = v;
                //没插入完全
                while (line>0) {
                    curPage = new Page(pageList.size(), false);
                    pageList.add(curPage);
                    line = addAreaToPage(area, curPage, line);
                }
            }
        }
        return pageList;
    }
}
